package com.edward.puff.execute;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.edward.puff.config.PuffConfiguration;
import com.edward.puff.config.PuffMetadata;
import com.edward.puff.repository.PuffTransLog;
import com.edward.puff.repository.TransLogRepository;
import com.edward.puff.utils.SpringProxyBeanUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;

/**
 * @Author: Edward
 * @Date: 2019/11/29 下午2:40
 * <p>
 * puff执行器,负责真实执行被@puff拦截的方法
 */
@Slf4j
public class PuffExecutor {

    @Resource
    private PuffConfiguration puffConfig;

    /**
     * 真正执行分布式调用
     */
    public void execute(PuffTransLog txLog) {
        PuffMetadata metadata = puffConfig.getPuffMetadataBySign(txLog.getSignature());
        TransLogRepository repository = metadata.getRepositoryClass() == null
                ? puffConfig.getGlobalRepository() : puffConfig.getBean(metadata.getRepositoryClass());
        try {
            //参数造型
            JSONArray params = JSONArray.parseArray(txLog.getCurParams());
            metadata.getArgsTypeIndexes().forEach(arg -> {
                JSONObject param = params.getJSONObject(arg.getArgIndex());
                params.set(arg.getArgIndex(), param.toJavaObject(arg.getArgClass()));
            });
            //利用反射执行(获取真实被代理对象,避免puffAspect再次拦截)
            Object target = SpringProxyBeanUtil.getTarget(puffConfig.getBean(metadata.getClazz()));
            Method method = metadata.getMethod();
            method.invoke(target, params.toArray());
            //完结txLog重试
            repository.finishTransLog(txLog.getTxLogId());
        } catch (Exception e) {
            log.error("txLog execute fail,txLog:{}", txLog, e);
            //更新txLog
            repository.updateTransLog(txLog.getTxLogId());
        }
    }

    public void asyncExecute(PuffTransLog txLog) {
        //获取线程池
        PuffMetadata meta = puffConfig.getPuffMetadataBySign(txLog.getSignature());
        Executor executor = StringUtils.isEmpty(meta.getExecutorBeanName())
                ? puffConfig.getGlobalExecutor() : (Executor) puffConfig.getBean(meta.getExecutorBeanName());
        //异步执行
        CompletableFuture.runAsync(() -> {
            execute(txLog);
        }, executor);
    }
}
